package com.lin.filegraph.utils.cluster;

import com.lin.filegraph.utils.compdg.ComponentEdge;
import com.lin.filegraph.utils.compdg.ComponentGraph;
import com.lin.filegraph.utils.compdg.ComponentNode;
import com.lin.filegraph.utils.componentRule.ComponentDepend;
import com.lin.filegraph.utils.componentRule.ComponentRules;
import com.lin.filegraph.utils.componentRule.Region;
import com.lin.filegraph.utils.model.ComponentModule;
import com.lin.filegraph.utils.model.Module;
import com.lin.filegraph.utils.name.ComRename;
import com.lin.filegraph.utils.name.DirSimilarity;
import com.lin.filegraph.utils.storage.ModuleStorage;
import com.lin.filegraph.utils.threshold.Threshold;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @description:
 * @author: linhuaixu
 * @time: 2023/5/18 15:50
 */

public class Cluster {

    public static void executeCluter(ComponentGraph graph, List<Module> comList, List<ComponentModule> fileList, List<ComponentDepend> comdList) {
        // 下限3-15
        // double minNum = Threshold.getMinComNum(graph.getTotalSize());
        // 上限8-30
        int maxNum = Threshold.getMaxComNum(graph.getTotalSize());

        System.out.println("预测规模：" + maxNum);
        // 组件数量
        graph.setModelSize(graph.getAllComponents().size());
        graph.setSumDependStrength();

        //聚合亲属节点
//		cn.seu.architecture.main.Timer timer =new cn.seu.architecture.main.Timer();
        NeighbourRank.neighbourUnion(graph);
//		timer.endTimer("SimRank ");

        System.out.println("*********聚类******");
        clusterGraph(graph, maxNum, comList, fileList, comdList);
        clusterPost(graph, maxNum, comList, fileList, comdList);
        NeighbourRank.neighbourUnion(graph);
        // if(graph.getNEmptyNum()>maxNum)

    }

    /**
     * 依据图聚类，预计规模5-30；
     */
    private static void clusterGraph(ComponentGraph graph, double maxNum, List<Module> comList, List<ComponentModule> fileList, List<ComponentDepend> comdList) {
        int remindNum = graph.getNEmptyNum();
//		cn.seu.architecture.main.Timer timer =new cn.seu.architecture.main.Timer();
        findCenter(graph);
//		timer.endTimer("findCenter ");
        boolean isMerge = false;
        graph.setAllCheck();
        if (remindNum > maxNum) {
            System.out.println("中心聚类.....");
            long time = System.currentTimeMillis();
            Map<ComponentNode, ComponentNode> kMap = new LinkedHashMap();
            searchRecentNode(graph, kMap);
//			timer.endTimer("searchRecentNode ");
            if (kMap != null) {
//				timer=new cn.seu.architecture.main.Timer();
                for (Map.Entry<ComponentNode, ComponentNode> entry : kMap.entrySet()) {
                    ComponentNode cn1 = entry.getKey();
                    ComponentNode cn2 = entry.getValue();
                    if (cn2 != null && cn1 != null && !cn2.equals(cn1)&&isRequred(cn1, cn2, graph.getTotalSize()))
                        if(ClusterPreJudge.enableCluster(cn1, cn2, graph)&&cn1.isFeature()==false&&cn2.isFeature()==false){
                            graph.mergeNodes(cn2, cn1);
                            isMerge = true;
                            //						cn2.reComName(ComRename.getComNameByDir(cn2));
                            ComRename.renameNode(graph, cn2);
                        }
                }
//				timer.endTimer("mergeMap:");
                if ((graph.getModelSize() - graph.getAllComponents().size()) > graph.getModelSize() * Threshold.DISPLAYPercent) {
                    ModuleStorage.cacheModuleGraph(graph, comList, fileList, comdList, 2);
                    graph.setModelSize(graph.getAllComponents().size());
                }
            }
//			System.out.println("找到依赖耗时：" + (System.currentTimeMillis() - time));
            remindNum = graph.getNEmptyNum();
        }
        if (isMerge) {
//			timer=new cn.seu.architecture.main.Timer();
            ComponentRules.executeRule2(graph);
//			timer.endTimer("rule:");
            clusterGraph(graph, maxNum, comList, fileList, comdList);
        } else {
            System.out.println("1-当前非空组件数量：" + remindNum);
            System.out.println("******************");
        }
    }

    /**
     * 依据图聚类，预计规模5-30；
     */
    private static void clusterPost(ComponentGraph graph, double maxNum, List<Module> comList, List<ComponentModule> fileList, List<ComponentDepend> comdList) {
        int remindNum = graph.getNEmptyNum();
        boolean isMerge = false;
        graph.setAllCheck();
        if (remindNum > maxNum) {
            System.out.println("后中心聚类.....");
            long time = System.currentTimeMillis();
            Map<ComponentNode, ComponentNode> kMap = new LinkedHashMap();
            searchSimNode(graph, kMap);
            if (kMap != null) {
                for (Map.Entry<ComponentNode, ComponentNode> entry : kMap.entrySet()) {

                    ComponentNode cn1 = entry.getKey();
                    ComponentNode cn2 = entry.getValue();
                    if (cn2 != null && cn1 != null && !cn2.equals(cn1) &&isRequred(cn1, cn2, graph.getTotalSize()))
                        if(ClusterPreJudge.enableCluster(cn1, cn2, graph)&&cn1.isFeature()==false&&cn2.isFeature()==false){
                            graph.mergeNodes(cn2, cn1);
                            isMerge = true;
                            ComRename.renameNode(graph, cn2);
                        }
                }
                if ((graph.getModelSize() - graph.getAllComponents().size()) > graph.getModelSize() * Threshold.DISPLAYPercent) {
                    ModuleStorage.cacheModuleGraph(graph, comList, fileList, comdList, 2);
                    graph.setModelSize(graph.getAllComponents().size());
                }
            }
//			System.out.println("找到依赖" + (System.currentTimeMillis() - time));
            remindNum = graph.getNEmptyNum();
        }
        if (isMerge) {
//			ComRename.renameCDG(graph);
            ComponentRules.executeRule2(graph);
            clusterPost(graph, maxNum, comList, fileList, comdList);
        } else {
            System.out.println("2-当前非空组件数量：" + remindNum);
            System.out.println("******************");
        }
    }

    /**
     * 寻找K中心
     */
    private static void findCenter(ComponentGraph graph) {
        // 寻找K中心
        int remindNum = graph.getNEmptyNum();
        Region.findRegion(remindNum, graph);
    }

    /**
     * 计算模块度，ΔQ=[ki-in/2m−∑tot*ki/2m2]
     * Σtot表示与社区c内的节点相连的边的权重之和
     * ki-in是社区c内节点与节点i的边权重之和
     * ki表示所有与节点i相连的边的权重之和
     */
    private static ComponentNode getNodeByModularity(ComponentGraph graph,ComponentNode centerNode) {
        double deltaQ=Double.MIN_VALUE;
        double m= graph.getSumDependStrength();
        double tot=0;
        ComponentNode recentNode=null;
        Set<ComponentNode> neibhCenter=graph.getNeighbours(centerNode);
        for (ComponentNode cn: neibhCenter) {
            tot+=graph.getCompEdgeDependence(cn, centerNode);
        }
        for (ComponentNode cn: neibhCenter) {
            if (isRequred(cn, centerNode, graph.getTotalSize()))
                if (!cn.isRegion()&&!cn.isIscheck()&&!centerNode.equals(cn)) {
                    double ki=0;
                    double kiin=graph.getCompEdgeDependence(cn, centerNode);
                    List<ComponentEdge> edges=graph.getNeighboursEdge(cn);
                    for(ComponentEdge ce:edges)
                        ki+=ce.getDependence();
                    double size= Math.abs(centerNode.getAllFiles().size()-cn.getAllFiles().size());  //Math.pow(
                    double dirSim =Math.max(DirSimilarity.getSimLevelOfNode(cn, centerNode),0.1);
                    double tempQ=((kiin/2/m)-(tot*ki/2/m/m))*dirSim;
                    if(tempQ>0&&tempQ>deltaQ)
                        if(ClusterPreJudge.enableCluster(centerNode, cn, graph)){
                            deltaQ=tempQ;
                            recentNode=cn;
                        }
                }
        }
        return recentNode;
    }



    /**
     * 寻找最近的点
     */
    private static void searchRecentNode(ComponentGraph graph, Map<ComponentNode, ComponentNode> kMap) {
//		cn.seu.architecture.main.Timer timer =new cn.seu.architecture.main.Timer();
//		DependRank dRank = new DependRank(graph);
//		timer.endTimer("dependrnk ");

        for (ComponentNode cn : graph.getAllComponents()) {
            if (cn.isRegion()) {
                double distance = 0;
                ComponentNode recentNode = null;
                recentNode=getNodeByModularity(graph, cn);

//				double sumDistance = 0;
//				List<ComponentEdge> outEdge = graph.getOutEdgesOfComp(cn);
//				for (ComponentEdge ce : outEdge) {
//					ComponentNode post = ce.getPostComponent();
//					if (isRequred(cn, post, graph.getTotalSize()))
//						if (!post.isRegion()&&!post.isIscheck()&&!post.equals(cn)) {
////							double rank = dRank.getRankOfNode(cn, post);
//							double depend = ClusterDistance.getIntense(ce, graph);
//							depend = Math.max(0, depend);
//							if (depend > 0)
//								if (depend > distance || (depend == distance && recentNode.getAllFiles().size() > post.getAllFiles().size())) {
//									if (ClusterPreJudge.enableCluster(post, cn, graph)) {
//										distance = depend;
//										recentNode = post;
//									}
//								}
//						}
//				}
//				List<ComponentEdge> inEdge = graph.getInEdgesOfComp(cn);
//				for (ComponentEdge ce : inEdge) {
//					ComponentNode pre = ce.getPreComponent();
//					if (isRequred(cn, pre, graph.getTotalSize())&&!pre.isIscheck()&&!pre.equals(cn))
//						if (!pre.isRegion() && DirSimilarity.getSimLevelOfNode(cn, pre) > 0) {
////							double rank = dRank.getRankOfNode(pre, cn);
//							double depend = ClusterDistance.getIntense(ce, graph);
//							depend = Math.max(0, depend);
//							if (depend > 0)
//								if (depend > distance || (depend == distance && recentNode.getAllFiles().size() > pre.getAllFiles().size())) {
//									if (ClusterPreJudge.enableCluster(pre, cn, graph)) {
//										distance = depend;
//										recentNode = pre;
//									}
//								}
//						}
//				}
////				timer.endTimer("outEdge:");

                if (recentNode != null) {
                    kMap.put(cn, recentNode);
                    recentNode.setIscheck(true);
                }
            }
        }
    }

    /**
     * 寻找相似的最小点
     */
    private static void searchSimNode(ComponentGraph graph, Map<ComponentNode, ComponentNode> kMap) {
        for (ComponentNode cn : graph.getAllComponents()) {
            //只处理小组件
            if (cn.getAllFiles().size() <= Threshold.MINCOM) {
                double distance = 0;
                ComponentNode recentNode = null;
                // List<ComponentEdge> outEdge=graph.getOutEdgesOfComp(cn);
                // for(ComponentEdge ce:outEdge){
                // ComponentNode post=ce.getPostComponent();
                // if(post.getAllFiles().size()>Threshold.MINCOM){
                // double depend= ClusterDistance.getIntense(ce, graph);
                // if(depend>0&&DirSimilarity.getSimLevelOfNode(cn, post)>0)
                // if(recentNode==null||recentNode.getAllFiles().size()>post.getAllFiles().size()||(recentNode.getAllFiles().size()==post.getAllFiles().size()&&depend>distance)){
                // if(ClusterPreJudge.enableCluster(post, cn, graph)){
                // distance=depend;
                // recentNode=post;
                // }
                // }
                // }
                // }
                List<ComponentEdge> inEdge = graph.getInEdgesOfComp(cn);
                for (ComponentEdge ce : inEdge) {
                    ComponentNode pre = ce.getPreComponent();
                    //不超过规模或者
                    if (isRequred(cn, pre, graph.getTotalSize())&&!pre.isIscheck()&&!pre.equals(cn)) {
                        double depend = ClusterDistance.getIntense(ce, graph);
                        if (depend > 0 && DirSimilarity.getSimLevelOfNode(cn, pre) > 0)
                            if (recentNode == null || recentNode.getAllFiles().size() > pre.getAllFiles().size() || (recentNode.getAllFiles().size() == pre.getAllFiles().size() && depend > distance)) {
                                if (ClusterPreJudge.enableCluster(pre, cn, graph)) {
                                    distance = depend;
                                    recentNode = pre;
                                }
                            }
                    }
                }

                if (recentNode != null) {
                    kMap.put(cn, recentNode);
                    recentNode.setIscheck(true);
                }
            }

        }
    }


    private static boolean isRequred(ComponentNode cn1,ComponentNode cn2,int size){
        if ((cn2.getAllFiles().size() + cn1.getAllFiles().size()) <= size* Threshold.COMSCALE)
            return true;
//		if(cn1.getAllFiles().size()<Threshold.MINCOM||cn2.getAllFiles().size()<Threshold.MINCOM)
//			return true;
        return false;
    }

}

